library(here)
Warning message:
In do_once((if (is_R_CMD_check()) stop else warning)("The function xfun::isFALSE() will be deprecated in the future. Please ",  :
  The function xfun::isFALSE() will be deprecated in the future. Please consider using base::isFALSE(x) or identical(x, FALSE) instead.
library(cowplot)
source(here("utils/data_processing.R"))
source(here("utils/figures.R"))
all_models <- list.files(here("data/processed_diagnoses"), pattern = "gz$") %>% 
  str_split("diagnoses_|_icd|.csv") %>% 
  sapply(., function(x) x[2]) %>% 
  unique()
all_models
[1] "claude-3-haiku-20240307_t1-0"       "claude-3-opus-20240229_t1-0"       
[3] "gemini-1.0-pro-002_t1-0"            "gemini-1.5-flash-preview-0514_t1-0"
[5] "gemini-1.5-pro-001_t1-0"            "gpt-3.5-turbo-1106"                
[7] "gpt-4-turbo-preview"               

Import data

df_gpt3.5 <- read_model("gpt-3.5-turbo-1106", icd = FALSE)
df_gpt4.0 <- read_model("gpt-4-turbo-preview", icd = FALSE)
df_claude3_haiku_t1.0 <- read_model("claude-3-haiku-20240307_t1-0", icd = FALSE)
df_claude3_opus_t1.0 <- read_model("claude-3-opus-20240229_t1-0", icd = FALSE)
df_gemini1.0_pro_t1.0 <- read_model("gemini-1.0-pro-002_t1-0", icd = FALSE)
df_gemini1.5_pro_t1.0 <- read_model("gemini-1.5-pro-001_t1-0", icd = FALSE)
df_gpt3.5_icd <- read_model("gpt-3.5-turbo-1106", icd = TRUE)
df_gpt4.0_icd <- read_model("gpt-4-turbo-preview", icd = TRUE)
df_claude3_haiku_t1.0_icd <- read_model("claude-3-haiku-20240307_t1-0", icd = TRUE)
df_claude3_opus_t1.0_icd <- read_model("claude-3-opus-20240229_t1-0", icd = TRUE)
df_gemini1.0_pro_t1.0_icd <- read_model("gemini-1.0-pro-002_t1-0", icd = TRUE)
df_gemini1.5_pro_t1.0_icd <- read_model("gemini-1.5-pro-001_t1-0", icd = TRUE)

Rank abundance

Original responses

rank_abundance_plot(df_gpt3.5)+ggtitle("ChatGPT 3.5")

rank_abundance_plot(df_gpt4.0)+ggtitle("ChatGPT 4.0")

rank_abundance_plot(df_claude3_haiku_t1.0)+ggtitle("Claude3 Haiku t1.0")

rank_abundance_plot(df_claude3_opus_t1.0)+ggtitle("Claude3 Opus")

rank_abundance_plot(df_gemini1.0_pro_t1.0)+ggtitle("Gemini 1.0 Pro")

rank_abundance_plot(df_gemini1.5_pro_t1.0)+ggtitle("Gemini 1.5 Pro")

ICD converted responses

rank_abundance_plot(df_gpt3.5_icd)+ggtitle("ChatGPT 3.5 ICD")

rank_abundance_plot(df_gpt4.0_icd)+ggtitle("ChatGPT 4.0 ICD")

rank_abundance_plot(df_claude3_haiku_t1.0_icd)+ggtitle("Claude3 Haiku ICD")

rank_abundance_plot(df_claude3_opus_t1.0_icd)+ggtitle("Claude3 Opus ICD")

rank_abundance_plot(df_gemini1.0_pro_t1.0_icd)+ggtitle("Gemini 1.0 Pro ICD")

rank_abundance_plot(df_gemini1.5_pro_t1.0_icd)+ggtitle("Gemini 1.5 Pro ICD")

Combined model data

multi_ranked_abundance_plot(df_gpt3.5, df_gpt4.0, df_claude3_haiku_t1.0, 
                            df_claude3_opus_t1.0, df_gemini1.0_pro_t1.0,
                            df_gemini1.5_pro_t1.0)+
  ggtitle("Combined model rank abundance", "Original responses")
Warning: The `fun.y` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
Please use the `fun` argument instead.Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

multi_ranked_abundance_plot(df_gpt3.5_icd, df_gpt4.0_icd, df_claude3_haiku_t1.0_icd, 
                            df_claude3_opus_t1.0_icd, df_gemini1.0_pro_t1.0_icd, 
                            df_gemini1.5_pro_t1.0_icd)+
  ggtitle("Combined model rank abundance", "ICD converted responses")

Top diagnoses plots

custom_labeler <- function(x, wrap_width=33) {
    x %>%
        str_replace("___.+$", "") %>%
        str_wrap(width = wrap_width)
}

custom_text_formatting <- list(
  theme(axis.text = element_text(size = 7, lineheight = 0.7), 
          strip.text = element_text(size = 7),
          axis.title = element_text(size = 9)),
  tidytext::scale_x_reordered(labels = ~custom_labeler(., wrap_width = 45))
)
n_diag <- 25
sub <- "Original responses"
top_diagnosis_plot(df_gpt3.5, n_diag = n_diag)+ggtitle("ChatGPT 3.5", sub)

top_diagnosis_plot(df_gpt4.0, n_diag = n_diag)+ggtitle("ChatGPT 4.0", sub)

top_diagnosis_plot(df_claude3_haiku_t1.0, n_diag = n_diag)+ggtitle("Claude3 Haiku t1.0", sub)

top_diagnosis_plot(df_claude3_opus_t1.0, n_diag = n_diag)+ggtitle("Claude3 Opus t1.0", sub)

top_diagnosis_plot(df_gemini1.0_pro_t1.0, n_diag = n_diag)+ggtitle("Gemini 1.0 Pro", sub)

top_diagnosis_plot(df_gemini1.5_pro_t1.0, n_diag = n_diag)+ggtitle("Gemini 1.5 Pro", sub)

n_diag <- 25
sub <- "ICD converted responses"
top_diagnosis_plot(df_gpt3.5_icd, n_diag = n_diag) + custom_text_formatting + ggtitle("ChatGPT 3.5 ICD", sub) 
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

top_diagnosis_plot(df_gpt4.0_icd, n_diag = n_diag)+ custom_text_formatting+ggtitle("ChatGPT 4.0 ICD", sub)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

top_diagnosis_plot(df_claude3_haiku_t1.0_icd, n_diag = n_diag)+ custom_text_formatting+ggtitle("Claude3 Haiku t1.0 ICD", sub)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

top_diagnosis_plot(df_claude3_opus_t1.0_icd, n_diag = n_diag)+ custom_text_formatting+ggtitle("Claude3 Opus t1.0 ICD", sub)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

top_diagnosis_plot(df_gemini1.0_pro_t1.0_icd, n_diag = n_diag)+ custom_text_formatting+ggtitle("Gemini 1.0 Pro ICD", sub)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

top_diagnosis_plot(df_gemini1.5_pro_t1.0_icd, n_diag = n_diag)+ custom_text_formatting+ggtitle("Gemini 1.5 Pro ICD", sub)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

multi_top_diagnosis_plot(distribution_vis = "points", wrap_width=45, n_diag = 25,
                         df_gpt3.5, df_gpt4.0, df_claude3_haiku_t1.0, 
                         df_claude3_opus_t1.0, df_gemini1.0_pro_t1.0,
                         df_gemini1.5_pro_t1.0)

plt_diag_icd <- multi_top_diagnosis_plot(distribution_vis = "points", wrap_width = 33, n_diag = 15,
                         df_gpt3.5_icd, df_gpt4.0_icd, df_claude3_haiku_t1.0_icd, 
                         df_claude3_opus_t1.0_icd, df_gemini1.0_pro_t1.0_icd, 
                         df_gemini1.5_pro_t1.0_icd) +
  guides(size = guide_legend(override.aes = list(size = 2)))

plt_diag_icd

plt_diag_icd$data %>% 
  summarise(freq=mean(freq),.by=c("criteria","diagnosis")) %>% 
  arrange(criteria, desc(freq))

Cumulative top frequency plots

sub <- "Original responses"
cumulative_frequency_plot(df_gpt3.5)$plot+ggtitle("GPT3", sub)

cumulative_frequency_plot(df_gpt4.0)$plot+ggtitle("GPT4", sub)

cumulative_frequency_plot(df_claude3_haiku_t1.0)$plot+ggtitle("Claude3 Haiku", sub)

cumulative_frequency_plot(df_claude3_opus_t1.0)$plot+ggtitle("Claude3 Haiku", sub)

cumulative_frequency_plot(df_gemini1.0_pro_t1.0)$plot+ggtitle("Gemini Pro 1.0", sub)

cumulative_frequency_plot(df_gemini1.5_pro_t1.0)$plot+ggtitle("Gemini Pro 1.5", sub)

sub <- "ICD converted responses"
cumulative_frequency_plot(df_gpt3.5_icd)$plot+ggtitle("GPT3 ICD", sub)

cumulative_frequency_plot(df_gpt4.0_icd)$plot+ggtitle("GPT4 ICD", sub)

cumulative_frequency_plot(df_claude3_haiku_t1.0_icd)$plot+ggtitle("Claude3 Haiku ICD", sub)

cumulative_frequency_plot(df_claude3_opus_t1.0_icd)$plot+ggtitle("Claude3 Haiku ICD", sub)

cumulative_frequency_plot(df_gemini1.0_pro_t1.0_icd)$plot+ggtitle("Gemini Pro 1.0 ICD", sub)

cumulative_frequency_plot(df_gemini1.5_pro_t1.0_icd)$plot+ggtitle("Gemini Pro 1.0 ICD", sub)

plt_freq <- multi_cumulative_frequency_plot(
  n_diagnoses = 25,
  distribution_vis = "points",
  df_gpt3.5,
  df_gpt4.0,
  df_claude3_haiku_t1.0,
  df_claude3_opus_t1.0,
  df_gemini1.0_pro_t1.0,
  df_gemini1.5_pro_t1.0
) +
  ggtitle("Original responses")

plt_freq

plt_freq$data %>% summarise(freq = mean(total_frequency), .by = "criteria")
plt_freq_icd <- multi_cumulative_frequency_plot(
  n_diagnoses = 25,
  distribution_vis = "points",
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  ggtitle("ICD converted responses")

plt_freq_icd

plt_freq_icd$data %>% summarise(freq = mean(total_frequency), .by = "criteria")

Diagnosis rank table

diagnosis_rank_table(df_gpt3.5, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60))
diagnosis_rank_table(df_gpt4.0, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_claude3_haiku_t1.0, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_claude3_opus_t1.0, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gemini1.0_pro_t1.0, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gemini1.5_pro_t1.0, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gpt3.5_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gpt4.0_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_claude3_haiku_t1.0_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_claude3_opus_t1.0_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gemini1.0_pro_t1.0_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
diagnosis_rank_table(df_gemini1.5_pro_t1.0_icd, "mast |mastoc|anaphylaxis") %>% mutate(diagnosis = substr(diagnosis, 1, 60)) 
rank_table <-
  multi_diagnosis_rank_table(
    search_pattern = "T78\\.2 |D47\\.02 |D89\\.41 |D89\\.49 |D89\\.4 ",
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  )
rank_table  
rank_table %>% 
  flextable() %>% 
  width(width = 30) %>% 
  align(j = 2:3, align = "center", part = "all")

Diagnosis

MCAS - Consortium

MCAS - Alternative

T78.2 Anaphylactic shock, unspecified

1
[1, 1, 1, 1, 1, 1]

132
[216, 87, 99, 174, 141, 77]

D47.02 Systemic mastocytosis

9
[22, 8, 7, 2, 14, 2]

50
[92, 78, 25, 41, 46, 19]

D89.41 Monoclonal mast cell activation syndrome

70
[128, 22, 28, 11, 179, 51]

74
[141, 64, 22, 37, 168, 12]

D89.49 Other mast cell activation disorder

234
[308, 62, 101, 140, 478, 318]

625
[1155, 568, 178, 467, 1109, 275]

D89.4 Mast cell activation syndrome and related disorders

496
[726, 174, NA, NA, 605, 478]

1423
[NA, 833, 1850, 1594, 1906, 933]

Diversity

multi_shannon_plot(
  distribution_vis = "points",
  wrap_width = 45,
  n_diag = 25,
  df_gpt3.5,
  df_gpt4.0,
  df_claude3_haiku_t1.0,
  df_claude3_opus_t1.0,
  df_gemini1.0_pro_t1.0,
  df_gemini1.5_pro_t1.0
)

plt_div_icd <- multi_shannon_plot(
  distribution_vis = "points",
  wrap_width = 45,
  n_diag = 25,
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
)

plt_div_icd

plt_div_icd$data %>% summarise(shannon=mean(shannon),.by="criteria")
extract_ggpubr_pvalues(plt_div_icd)  

Similarity

diagnosis_similarity_heatmap(df_gpt3.5, method = "bray")

diagnosis_similarity_heatmap(df_gpt4.0, method = "bray")

diagnosis_similarity_heatmap(df_claude3_haiku_t1.0, method = "bray")

diagnosis_similarity_heatmap(df_claude3_opus_t1.0, method = "bray")

diagnosis_similarity_heatmap(df_gemini1.0_pro_t1.0, method = "bray")

diagnosis_similarity_heatmap(df_gemini1.5_pro_t1.0, method = "bray")

diagnosis_similarity_heatmap(df_gpt3.5_icd, method = "bray")

diagnosis_similarity_heatmap(df_gpt4.0_icd, method = "bray")

diagnosis_similarity_heatmap(df_claude3_haiku_t1.0_icd, method = "bray")

diagnosis_similarity_heatmap(df_claude3_opus_t1.0_icd, method = "bray")

diagnosis_similarity_heatmap(df_gemini1.0_pro_t1.0_icd, method = "bray")

diagnosis_similarity_heatmap(df_gemini1.5_pro_t1.0_icd, method = "bray")

multi_diagnosis_similarity_heatmap(
  df_gpt3.5,
  df_gpt4.0,
  df_claude3_haiku_t1.0,
  df_claude3_opus_t1.0,
  df_gemini1.0_pro_t1.0,
  df_gemini1.5_pro_t1.0,
  method = "bray",
  show_dend = F,
  label_size = 6,
  title_size = 9
)

multi_diagnosis_similarity_heatmap(
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd,
    method = "bray",
  show_dend = F,
  label_size = 6,
  title_size = 9
)

  • Bray-Curtis similarity measures the similarity of a given diagnostic criteria’s set of alternative diagnoses along with their frequencies.
  • This demonstrates that SLE criteria results in a very similar set and frequency of diagnoses, while the diagnoses associated with two MCAS criteria are as different from each other as they are from those generated by the criteria of other conditions.

Multi Bray Curtis Similarity

calculate_similarity <- function(df){
  df <- df %>% 
    rename(model = original_df) %>% 
    count(model, criteria, diagnosis) %>% 
    unite(criteria, criteria, model)
  
  table(df$criteria, df$diagnosis) %>% 
  vegan::vegdist(method = "bray") %>% 
  as.matrix() %>% 
  {1-.} 
}
combine_data_frames(
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) %>% 
  calculate_similarity() %>% 
  model_criteria_heatmap(., 
                color_scale = viridis::viridis(3), 
                title = " ", 
                metric = "Bray-Curtis\nsimilarity", 
                symmetric = F,
                font_size = 8) 

combine_data_frames(
  df_gpt3.5,
  df_gpt4.0,
  df_claude3_haiku_t1.0,
  df_claude3_opus_t1.0,
  df_gemini1.0_pro_t1.0,
  df_gemini1.5_pro_t1.0
) %>% 
  calculate_similarity() %>% 
  model_criteria_heatmap(., 
                color_scale = viridis::viridis(3), 
                title = " ", 
                metric = "Bray-Curtis\nsimilarity", 
                symmetric = F,
                font_size = 8) 

PCA

diagnosis_pca_plot(df_gpt3.5) + ggtitle("GPT3")

diagnosis_pca_plot(df_gpt4.0) + ggtitle("GPT4")

diagnosis_pca_plot(df_claude3_haiku_t1.0) + ggtitle("Claude Haiku")

diagnosis_pca_plot(df_claude3_opus_t1.0) + ggtitle("Claude Opus")

diagnosis_pca_plot(df_gemini1.0_pro_t1.0) + ggtitle("Gemini")

diagnosis_pca_plot(df_gemini1.5_pro_t1.0) + ggtitle("Gemini")

df <- listN(df_gpt3.5_icd, df_gpt4.0_icd, df_claude3_haiku_t1.0_icd, df_claude3_opus_t1.0_icd, df_gemini1.0_pro_t1.0_icd, df_gemini1.5_pro_t1.0_icd) %>% 
  mapply(function(x,y) {mutate(x, model=y)}, ., names(.), SIMPLIFY = F) %>% 
  bind_rows() %>% 
  count(model, criteria, diagnosis) %>% 
  pivot_wider(names_from = "diagnosis", values_from = "n", values_fill = 0) %>% 
  unite(id, model, criteria, sep = "__") %>% 
  column_to_rownames("id") %>% 
  prcomp(scale. = F)

as.data.frame(df$x) %>% 
    rownames_to_column("id") %>% 
  separate(id, into = c("model", "criteria"), sep = "__") %>% 
  format_criteria() %>% 
  format_models() %>% 
  ggplot(aes(x = PC1, y = PC2, color = criteria))+
    geom_point()+
    # ggrepel::geom_label_repel() +
    theme_bw() +
  scale_color_brewer(palette = "Dark2")

Precision

  • Precision represents how similar each iteration of a 10-point differential diagnosis is with all other differential diagnoses from the same set of criteria.
  • I.e. how reproducible the 10-point differential diagnosis is for each criteria
  • Measured by obtaining the Bray-Curtis similarity values between all iterations within a criteria
# Script for calculating all Bray-Curtis similarity values within a criteria
# Found in source(here("scripts/diversity_analysis/calculate_precision.R"))
# Calculate precision
library(here)
source(here("utils/data_processing.R"))

models <- list.files(here("data/processed_diagnoses"), pattern = "gz$") %>% 
  str_split("diagnoses_|_icd|.csv") %>% 
  sapply(., function(x) x[2]) %>% 
  unique()

use_icd <- TRUE

if (use_icd){models <- str_glue("{models}_icd")}

for (m in models){
  print(sprintf("READING IN DATA FOR: %s", m))
  read_path <- sprintf("data/processed_diagnoses/diagnoses_%s.csv.gz", m)
  df <- read_csv(here(read_path))
  
  print(sprintf("CALCULATING PRECISION FOR: %s", m))
  df <- calculate_precision(df)
  
  print(sprintf("WRITING PRECISION DATA FOR: %s", m))
  out_path <- sprintf("data/diversity_analysis/diagnosis_precision_%s.csv.gz", m)
  write_csv(df, here(out_path))
}
precision_dist_to_sim <- function(df){
  df %>% 
    mutate(
      mean = 1-mean,
      max = 1-min,
      min = 1-max
    )
}

plt_precision_icd <- read_csv(here("data/diversity_analysis/compiled_icd_diagnosis_precision.csv")) %>% 
  precision_dist_to_sim() %>% 
  format_criteria() %>% 
  format_models() %>%
  filter(model != "Gemini 1.5 Flash") %>% 
  ggplot(aes(x = criteria, y = mean))+
  theme_bw()+
  theme(axis.text.x = element_text(angle= 45, hjust = 1))+
  labs(x="", y = "Average Bray-Curtis Similarity") +
  ggpubr::geom_pwc(aes(group = criteria), method = "wilcox.test", p.adjust.method = "BH", hide.ns = T, label = "p.adj.signif", bracket.nudge.y = 0.3, vjust = 0.6, step.increase = 0.14, tip.length = 0.02) +
  labs(color=NULL)+
  scale_color_brewer(palette = "Dark2") +
  plot_selector("points")
Rows: 42 Columns: 8── Column specification ───────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): criteria, model
dbl (6): n, mean, max, min, sd, se
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
plt_precision_icd

plt_precision_icd$data %>% summarise(mean = mean(mean), .by="criteria") 
extract_ggpubr_pvalues(plt_precision_icd) 

iNEXT

inext_plots <- function(inext_obj){
  for (i in 1:3){
    plt <- iNEXT::ggiNEXT(inext_obj, type=i, facet.var="Assemblage", color.var="Assemblage") +
      theme_classic() + 
      scale_color_brewer(palette = "Set1") +
      theme(axis.text.x = element_text(angle = 90))+
      scale_color_brewer(palette = "Dark2")
    print(plt)
  }
}

readRDS(here("data/diversity_analysis/mcas_iNEXT_gpt4_e250000.RDS")) %>% inext_plots()
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

readRDS(here("data/diversity_analysis/mcas_iNEXT_dropSingle_gpt4_e200000.RDS")) %>% inext_plots()
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

readRDS(here("data/diversity_analysis/mcas_iNEXT_dropSingle_psuedoMinus_gpt4_e200000.RDS")) %>% inext_plots()
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

# custom_labeler <- function(x, wrap_width=33) {
#     x %>%
#         str_replace("___.+$", "") %>%
#         str_wrap(width = wrap_width)
# }

Final plot

Version 1

n_diagnoses_bar <- 10
n_diagnoses_abundance <- 50
n_diagnoses_cumulative <- 50

title_size <- 9
label_size <- 6
legend_x_pad <- 4
legend_y_pad <- 2

apply_text_formatting <- list(theme(
  axis.text = element_text(size = label_size),
  axis.title = element_text(size = title_size),
  legend.text = element_text(size = label_size),
  strip.text = element_text(size = label_size+1),
  legend.key.height = unit(0.4, 'cm'),
  legend.box.background = element_rect(color = "black", size = 1),
  legend.margin = margin(t = legend_y_pad, r = legend_x_pad, b = legend_y_pad, l = legend_x_pad*1.1),
  legend.spacing.x = unit(0, 'cm'),                           # Horizontal spacing between legend items
  # legend.spacing.y = unit(0, 'cm'),
  # legend.box.spacing = unit(0, "cm")
  ))
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
strip_margin <- 1
strip_formatting <- list(theme(
  strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)), 
  strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
  # strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))

plt_diags <-
  multi_top_diagnosis_plot(
    distribution_vis = "points",
    wrap_width = 58,
    n_diag = n_diagnoses_bar,
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  theme(axis.text.y = element_text(size = 6.5)) +
  strip_formatting +
  # theme(legend.position = c(-1,0))+
  theme(panel.spacing = unit(0, "lines")) +
  guides(color = guide_legend(override.aes = list(size = 2)))  # Increase the point size in the legend)
  

plt_rank <-
  multi_ranked_abundance_plot(
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 2))

plt_cumulative <- multi_cumulative_frequency_plot(
  n_diagnoses = n_diagnoses_cumulative,
  distribution_vis = "points",
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 2)) +
  labs(y = "Combined frequency\nof top 50 diagnoses", x = NULL)

plt_shannon <- multi_shannon_plot(
  distribution_vis = "points",
  wrap_width = 45,
  n_diag = 25,
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(ncol = 2))

plt_precision <- read_csv(here("data/diversity_analysis/compiled_icd_diagnosis_precision.csv")) %>% 
  precision_dist_to_sim() %>% 
  format_criteria() %>% 
  format_models() %>%
  filter(model != "Gemini 1.5 Flash") %>% 
  ggplot(aes(x = criteria, y = mean))+
  theme_bw()+
  theme(axis.text.x = element_text(angle= 45, hjust = 1))+
  labs(x="", y = "Average Bray-Curtis\nSimilarity") +
  ggpubr::geom_pwc(aes(group = criteria), method = "wilcox.test", p.adjust.method = "BH", hide.ns = T, label = "p.adj.signif", bracket.nudge.y = 0.3, vjust = 0.6, step.increase = 0.14, tip.length = 0.02) +
  labs(color=NULL)+
  scale_color_brewer(palette = "Dark2") +
  plot_selector("points") +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(ncol = 2))

full_plt <- plot_grid(
  
  ###
  plt_diags,
  ###
  NULL,
  plot_grid(
      plt_rank,
      plt_cumulative,
      plt_shannon,
      plt_precision,
      nrow = 1, 
      axis = 'tb',
      align = 'h',
      rel_widths = c(1, 0.7, 0.7, 0.7),
      labels = c(LETTERS[2:5]),
      vjust = 0.2
    ),
  ncol = 1,
  rel_heights = c(1.2, 0.05, 0.65),
  labels = c("A","","")
)  

full_plt

Version 2

n_diagnoses_bar <- 10
n_diagnoses_abundance <- 50
n_diagnoses_cumulative <- 50

title_size <- 9
label_size <- 6
legend_x_pad <- 4
legend_y_pad <- 2

apply_text_formatting <- list(theme(
  axis.text = element_text(size = label_size),
  axis.title = element_text(size = title_size),
  legend.text = element_text(size = label_size),
  strip.text = element_text(size = label_size+1),
  legend.key.height = unit(0.4, 'cm'),
  legend.box.background = element_rect(color = "black", size = 1),
  legend.margin = margin(t = legend_y_pad, r = legend_x_pad, b = legend_y_pad, l = legend_x_pad*1.1),
  legend.spacing.x = unit(0, 'cm'),                           # Horizontal spacing between legend items
  # legend.spacing.y = unit(0, 'cm'),
  # legend.box.spacing = unit(0, "cm")
  ))

strip_margin <- 1
strip_formatting <- list(theme(
  strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)), 
  strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
  # strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))

plt_diags <-
  multi_top_diagnosis_plot(
    distribution_vis = "points",
    wrap_width = 58,
    n_diag = n_diagnoses_bar,
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  theme(axis.text.y = element_text(size = 6.5)) +
  strip_formatting +
  # theme(legend.position = c(-1,0))+
  theme(panel.spacing = unit(0, "lines")) +
  guides(color = guide_legend(override.aes = list(size = 2), nrow = 1))  # Increase the point size in the legend)
  

plt_rank <-
  multi_ranked_abundance_plot(
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = c(0.7,0.7))+
  # theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 1)) +
  labs(color = NULL)
Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2 3.5.0.
Please use the `legend.position.inside` argument of `theme()` instead.
plt_cumulative <- multi_cumulative_frequency_plot(
  n_diagnoses = n_diagnoses_cumulative,
  distribution_vis = "points",
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 2)) +
  labs(y = "Combined frequency\nof top 50 diagnoses", x = NULL)

plt_shannon <- multi_shannon_plot(
  distribution_vis = "points",
  wrap_width = 45,
  n_diag = 25,
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(ncol = 2))

plt_precision <- read_csv(here("data/diversity_analysis/compiled_icd_diagnosis_precision.csv")) %>% 
  precision_dist_to_sim() %>% 
  format_criteria() %>% 
  format_models() %>%
  filter(model != "Gemini 1.5 Flash") %>% 
  ggplot(aes(x = criteria, y = mean))+
  theme_bw()+
  theme(axis.text.x = element_text(angle= 45, hjust = 1))+
  labs(x="", y = "Average Bray-Curtis\nSimilarity") +
  ggpubr::geom_pwc(aes(group = criteria), method = "wilcox.test", p.adjust.method = "BH", hide.ns = T, label = "p.adj.signif", bracket.nudge.y = 0.3, vjust = 0.6, step.increase = 0.14, tip.length = 0.02) +
  labs(color=NULL)+
  scale_color_brewer(palette = "Dark2") +
  plot_selector("points") +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(ncol = 2))

full_plt <- plot_grid(
  
  ###
  plt_diags,
  ###
  NULL,
  plot_grid(
      plt_rank,
      plot_grid(
        plot_grid(
          plt_shannon+ theme(legend.position="none"),
          plt_precision+ theme(legend.position="none"),
          nrow = 1,
          axis = 'tb',
          align = 'h'
        ),
        get_legend(plt_shannon+ guides(color = guide_legend(row = 1))),
        ncol = 1,
        rel_heights = c(1,0.1)
      ),
      nrow = 1, 
      rel_widths = c(1,1),
      # labels = c(LETTERS[2:5]),
      vjust = 0.2
    ),
  ncol = 1,
  rel_heights = c(1.2, 0.05, 0.65),
  labels = c("A","","")
)  
Warning: Multiple components found; returning the first one. To return all, use `return_all = TRUE`.
full_plt

Version 3

n_diagnoses_bar <- 10
n_diagnoses_abundance <- 50
n_diagnoses_cumulative <- 50

title_size <- 9
label_size <- 6
legend_x_pad <- 4
legend_y_pad <- 2

apply_text_formatting <- list(
  theme(
    axis.text = element_text(size = label_size),
    axis.title = element_text(size = title_size),
    legend.text = element_text(size = label_size, margin=margin(0,0,0,r=1)),
    strip.text = element_text(size = label_size + 1),
    legend.key.height = unit(0.4, 'cm'),
    legend.key.width = unit(0.4, 'cm'),
    # legend.key = element_rect(size =  margin(0,0,0,0)),
    legend.box.background = element_rect(color = "black", size = 1),
    legend.margin = margin(
      t = legend_y_pad,
      r = legend_x_pad,
      b = legend_y_pad,
      l = legend_x_pad
    ),
    legend.key.spacing.y = unit(-1.5, "pt"),
    legend.box.spacing = unit(5,"pt")
  )
)

strip_margin <- 1
strip_formatting <- list(theme(
  strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)), 
  strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
  # strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))

plt_diags <-
  multi_top_diagnosis_plot(
    distribution_vis = "points",
    wrap_width = 58,
    n_diag = n_diagnoses_bar,
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  theme(axis.text.y = element_text(size = 6.5)) +
  strip_formatting +
  # theme(legend.position = c(-1,0))+
  theme(panel.spacing = unit(0, "lines")) +
  guides(color = guide_legend(override.aes = list(size = 2), nrow = 1))  # Increase the point size in the legend)
  

plt_rank <-
  multi_ranked_abundance_plot(
    df_gpt3.5_icd,
    df_gpt4.0_icd,
    df_claude3_haiku_t1.0_icd,
    df_claude3_opus_t1.0_icd,
    df_gemini1.0_pro_t1.0_icd,
    df_gemini1.5_pro_t1.0_icd
  ) +
  theme(legend.position = c(0.7,0.7))+
  # theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 1)) +
  labs(color = NULL)

plt_cumulative <- multi_cumulative_frequency_plot(
  n_diagnoses = n_diagnoses_cumulative,
  distribution_vis = "points",
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  apply_text_formatting +
  guides(color = guide_legend(ncol = 2)) +
  labs(y = "Combined frequency\nof top 50 diagnoses", x = NULL)

plt_shannon <- multi_shannon_plot(
  distribution_vis = "points",
  wrap_width = 45,
  n_diag = 25,
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
) +
  apply_text_formatting +
  theme(
    # legend.position = "bottom", 
    # legend.direction = "horizontal", 
    axis.text.x = element_text(angle=90,hjust=1)) +
  guides(color = guide_legend(ncol = 2))

plt_precision <- read_csv(here("data/diversity_analysis/compiled_icd_diagnosis_precision.csv")) %>% 
  precision_dist_to_sim() %>% 
  format_criteria() %>% 
  format_models() %>%
  filter(model != "Gemini 1.5 Flash") %>% 
  ggplot(aes(x = criteria, y = mean))+
  theme_bw()+
  theme(axis.text.x = element_text(angle= 90, hjust = 1))+
  labs(x="", y = "Mean Bray-Curtis Similarity") +
  ggpubr::geom_pwc(aes(group = criteria), method = "wilcox.test", p.adjust.method = "BH", hide.ns = T, label = "p.adj.signif", bracket.nudge.y = 0.3, vjust = 0.6, step.increase = 0.14, tip.length = 0.02) +
  labs(color=NULL)+
  scale_color_brewer(palette = "Dark2") +
  plot_selector("points") +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(ncol = 2))

plt_similarity <- multi_diagnosis_similarity_heatmap(
  method = "bray",
  show_dend = T,
  dendrogram_weight = unit(2.5, "mm"),
  legend_label = "Bray-Curtis similarity",
  legend_direction = "horizontal",
  label_size = 6,
  title_size = 9,
  df_gpt3.5_icd,
  df_gpt4.0_icd,
  df_claude3_haiku_t1.0_icd,
  df_claude3_opus_t1.0_icd,
  df_gemini1.0_pro_t1.0_icd,
  df_gemini1.5_pro_t1.0_icd
)

full_plt <- plot_grid(
  ###
  plt_diags,
  ###
  NULL,
  plot_grid(
    NULL,
    plot_grid(
    grid::grid.grabExpr(ComplexHeatmap::draw(plt_similarity, heatmap_legend_side = 'bottom')),
    NULL,ncol=1,rel_heights = c(1,0.1)
    ),
    plot_grid(
    plt_rank,
    NULL,ncol=1,rel_heights = c(1,0.1)
    ),
    plot_grid(
      plot_grid(
        plt_shannon+ theme(legend.position="none"),
        plt_precision+ theme(legend.position="none"),
        nrow = 1,
        axis = 'tb',
        align = 'h'
      ),
      plot_grid(NULL,get_legend(plt_shannon),nrow=1,rel_widths=c(0.2,1)),
      NULL,
      ncol = 1,
      rel_heights = c(1,0.05,0.1)
    ),
    nrow = 1, 
    rel_widths = c(0.1,0.8, 1,0.9),
    vjust = 0.2
    ),
  ncol = 1,
  rel_heights = c(1.2, 0.07, 0.65)
)  

full_plt <- cowplot::ggdraw(full_plt)+cowplot::draw_plot_label(c("A","B","C","D","E"), x=c(0,0,0.35,0.67,0.83), y=c(1,0.38,0.38,0.38,0.38))
full_plt

  • Add a 2 column legend under D+E
ggsave(plot=full_plt,filename=here("figures/3_diagnosis_diversity.pdf"), width = 7.5, height = 8.5)

set_table_properties(opts_pdf = list(tabcolsep = 0))

set_flextable_defaults(fonts_ignore=TRUE)

multi_diagnosis_rank_table(search_pattern = "T78\\.2 |D47\\.02 |D89\\.41 |D89\\.49 |D89\\.4 ",
                                         df_gpt3.5_icd, df_gpt4.0_icd, df_claude3_haiku_t1.0_icd, df_claude3_opus_t1.0_icd, df_gemini1.0_pro_t1.0_icd, df_gemini1.5_pro_t1.0_icd) %>% 
  flextable() %>% 
  width(width = 2) %>% 
  fontsize(size = 9) %>% 
  fontsize(size = 10, part = "header") %>% 
  padding(padding = 0) %>% 
  align(j = 2:3, align = "center", part = "all") %>% 
  set_table_properties(opts_pdf = list(arraystretch = 1.25)) %>% 
  {print(., preview = "pdf");.}

Diagnosis

MCAS - Consortium

MCAS - Alternative

T78.2 Anaphylactic shock, unspecified

1
[1, 1, 1, 1, 1, 1]

132
[216, 87, 99, 174, 141, 77]

D47.02 Systemic mastocytosis

9
[22, 8, 7, 2, 14, 2]

50
[92, 78, 25, 41, 46, 19]

D89.41 Monoclonal mast cell activation syndrome

70
[128, 22, 28, 11, 179, 51]

74
[141, 64, 22, 37, 168, 12]

D89.49 Other mast cell activation disorder

234
[308, 62, 101, 140, 478, 318]

625
[1155, 568, 178, 467, 1109, 275]

D89.4 Mast cell activation syndrome and related disorders

496
[726, 174, NA, NA, 605, 478]

1423
[NA, 833, 1850, 1594, 1906, 933]

LS0tCnRpdGxlOiAiRGlhZ25vc2lzIGRpc3RyaWJ1dGlvbiBhbmFseXNpcyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmxpYnJhcnkoaGVyZSkKbGlicmFyeShjb3dwbG90KQpzb3VyY2UoaGVyZSgidXRpbHMvZGF0YV9wcm9jZXNzaW5nLlIiKSkKc291cmNlKGhlcmUoInV0aWxzL2ZpZ3VyZXMuUiIpKQpgYGAKCmBgYHtyfQphbGxfbW9kZWxzIDwtIGxpc3QuZmlsZXMoaGVyZSgiZGF0YS9wcm9jZXNzZWRfZGlhZ25vc2VzIiksIHBhdHRlcm4gPSAiZ3okIikgJT4lIAogIHN0cl9zcGxpdCgiZGlhZ25vc2VzX3xfaWNkfC5jc3YiKSAlPiUgCiAgc2FwcGx5KC4sIGZ1bmN0aW9uKHgpIHhbMl0pICU+JSAKICB1bmlxdWUoKQphbGxfbW9kZWxzCmBgYAojIEltcG9ydCBkYXRhCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmRmX2dwdDMuNSA8LSByZWFkX21vZGVsKCJncHQtMy41LXR1cmJvLTExMDYiLCBpY2QgPSBGQUxTRSkKZGZfZ3B0NC4wIDwtIHJlYWRfbW9kZWwoImdwdC00LXR1cmJvLXByZXZpZXciLCBpY2QgPSBGQUxTRSkKZGZfY2xhdWRlM19oYWlrdV90MS4wIDwtIHJlYWRfbW9kZWwoImNsYXVkZS0zLWhhaWt1LTIwMjQwMzA3X3QxLTAiLCBpY2QgPSBGQUxTRSkKZGZfY2xhdWRlM19vcHVzX3QxLjAgPC0gcmVhZF9tb2RlbCgiY2xhdWRlLTMtb3B1cy0yMDI0MDIyOV90MS0wIiwgaWNkID0gRkFMU0UpCmRmX2dlbWluaTEuMF9wcm9fdDEuMCA8LSByZWFkX21vZGVsKCJnZW1pbmktMS4wLXByby0wMDJfdDEtMCIsIGljZCA9IEZBTFNFKQpkZl9nZW1pbmkxLjVfcHJvX3QxLjAgPC0gcmVhZF9tb2RlbCgiZ2VtaW5pLTEuNS1wcm8tMDAxX3QxLTAiLCBpY2QgPSBGQUxTRSkKYGBgCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmRmX2dwdDMuNV9pY2QgPC0gcmVhZF9tb2RlbCgiZ3B0LTMuNS10dXJiby0xMTA2IiwgaWNkID0gVFJVRSkKZGZfZ3B0NC4wX2ljZCA8LSByZWFkX21vZGVsKCJncHQtNC10dXJiby1wcmV2aWV3IiwgaWNkID0gVFJVRSkKZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCA8LSByZWFkX21vZGVsKCJjbGF1ZGUtMy1oYWlrdS0yMDI0MDMwN190MS0wIiwgaWNkID0gVFJVRSkKZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkIDwtIHJlYWRfbW9kZWwoImNsYXVkZS0zLW9wdXMtMjAyNDAyMjlfdDEtMCIsIGljZCA9IFRSVUUpCmRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QgPC0gcmVhZF9tb2RlbCgiZ2VtaW5pLTEuMC1wcm8tMDAyX3QxLTAiLCBpY2QgPSBUUlVFKQpkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkIDwtIHJlYWRfbW9kZWwoImdlbWluaS0xLjUtcHJvLTAwMV90MS0wIiwgaWNkID0gVFJVRSkKYGBgCgoKIyBSYW5rIGFidW5kYW5jZQoKKipPcmlnaW5hbCByZXNwb25zZXMqKgpgYGB7cn0KcmFua19hYnVuZGFuY2VfcGxvdChkZl9ncHQzLjUpK2dndGl0bGUoIkNoYXRHUFQgMy41IikKcmFua19hYnVuZGFuY2VfcGxvdChkZl9ncHQ0LjApK2dndGl0bGUoIkNoYXRHUFQgNC4wIikKcmFua19hYnVuZGFuY2VfcGxvdChkZl9jbGF1ZGUzX2hhaWt1X3QxLjApK2dndGl0bGUoIkNsYXVkZTMgSGFpa3UgdDEuMCIpCnJhbmtfYWJ1bmRhbmNlX3Bsb3QoZGZfY2xhdWRlM19vcHVzX3QxLjApK2dndGl0bGUoIkNsYXVkZTMgT3B1cyIpCnJhbmtfYWJ1bmRhbmNlX3Bsb3QoZGZfZ2VtaW5pMS4wX3Byb190MS4wKStnZ3RpdGxlKCJHZW1pbmkgMS4wIFBybyIpCnJhbmtfYWJ1bmRhbmNlX3Bsb3QoZGZfZ2VtaW5pMS41X3Byb190MS4wKStnZ3RpdGxlKCJHZW1pbmkgMS41IFBybyIpCmBgYAoKKipJQ0QgY29udmVydGVkIHJlc3BvbnNlcyoqCmBgYHtyfQpyYW5rX2FidW5kYW5jZV9wbG90KGRmX2dwdDMuNV9pY2QpK2dndGl0bGUoIkNoYXRHUFQgMy41IElDRCIpCnJhbmtfYWJ1bmRhbmNlX3Bsb3QoZGZfZ3B0NC4wX2ljZCkrZ2d0aXRsZSgiQ2hhdEdQVCA0LjAgSUNEIikKcmFua19hYnVuZGFuY2VfcGxvdChkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkKStnZ3RpdGxlKCJDbGF1ZGUzIEhhaWt1IElDRCIpCnJhbmtfYWJ1bmRhbmNlX3Bsb3QoZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkKStnZ3RpdGxlKCJDbGF1ZGUzIE9wdXMgSUNEIikKcmFua19hYnVuZGFuY2VfcGxvdChkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkKStnZ3RpdGxlKCJHZW1pbmkgMS4wIFBybyBJQ0QiKQpyYW5rX2FidW5kYW5jZV9wbG90KGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QpK2dndGl0bGUoIkdlbWluaSAxLjUgUHJvIElDRCIpCmBgYAoKKipDb21iaW5lZCBtb2RlbCBkYXRhKioKCmBgYHtyfQptdWx0aV9yYW5rZWRfYWJ1bmRhbmNlX3Bsb3QoZGZfZ3B0My41LCBkZl9ncHQ0LjAsIGRmX2NsYXVkZTNfaGFpa3VfdDEuMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMCwgZGZfZ2VtaW5pMS4wX3Byb190MS4wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS41X3Byb190MS4wKSsKICBnZ3RpdGxlKCJDb21iaW5lZCBtb2RlbCByYW5rIGFidW5kYW5jZSIsICJPcmlnaW5hbCByZXNwb25zZXMiKQpgYGAKCmBgYHtyfQptdWx0aV9yYW5rZWRfYWJ1bmRhbmNlX3Bsb3QoZGZfZ3B0My41X2ljZCwgZGZfZ3B0NC4wX2ljZCwgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCkrCiAgZ2d0aXRsZSgiQ29tYmluZWQgbW9kZWwgcmFuayBhYnVuZGFuY2UiLCAiSUNEIGNvbnZlcnRlZCByZXNwb25zZXMiKQpgYGAKCiMgVG9wIGRpYWdub3NlcyBwbG90cwoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KY3VzdG9tX2xhYmVsZXIgPC0gZnVuY3Rpb24oeCwgd3JhcF93aWR0aD0zMykgewogICAgeCAlPiUKICAgICAgICBzdHJfcmVwbGFjZSgiX19fLiskIiwgIiIpICU+JQogICAgICAgIHN0cl93cmFwKHdpZHRoID0gd3JhcF93aWR0aCkKfQoKY3VzdG9tX3RleHRfZm9ybWF0dGluZyA8LSBsaXN0KAogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNywgbGluZWhlaWdodCA9IDAuNyksIAogICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSksCiAgdGlkeXRleHQ6OnNjYWxlX3hfcmVvcmRlcmVkKGxhYmVscyA9IH5jdXN0b21fbGFiZWxlciguLCB3cmFwX3dpZHRoID0gNDUpKQopCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOH0Kbl9kaWFnIDwtIDI1CnN1YiA8LSAiT3JpZ2luYWwgcmVzcG9uc2VzIgp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfZ3B0My41LCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkNoYXRHUFQgMy41Iiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfZ3B0NC4wLCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkNoYXRHUFQgNC4wIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfY2xhdWRlM19oYWlrdV90MS4wLCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkNsYXVkZTMgSGFpa3UgdDEuMCIsIHN1YikKdG9wX2RpYWdub3Npc19wbG90KGRmX2NsYXVkZTNfb3B1c190MS4wLCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkNsYXVkZTMgT3B1cyB0MS4wIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfZ2VtaW5pMS4wX3Byb190MS4wLCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkdlbWluaSAxLjAgUHJvIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfZ2VtaW5pMS41X3Byb190MS4wLCBuX2RpYWcgPSBuX2RpYWcpK2dndGl0bGUoIkdlbWluaSAxLjUgUHJvIiwgc3ViKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxNiwgZmlnLmhlaWdodCA9IDEwfQpuX2RpYWcgPC0gMjUKc3ViIDwtICJJQ0QgY29udmVydGVkIHJlc3BvbnNlcyIKdG9wX2RpYWdub3Npc19wbG90KGRmX2dwdDMuNV9pY2QsIG5fZGlhZyA9IG5fZGlhZykgKyBjdXN0b21fdGV4dF9mb3JtYXR0aW5nICsgZ2d0aXRsZSgiQ2hhdEdQVCAzLjUgSUNEIiwgc3ViKSAKdG9wX2RpYWdub3Npc19wbG90KGRmX2dwdDQuMF9pY2QsIG5fZGlhZyA9IG5fZGlhZykrIGN1c3RvbV90ZXh0X2Zvcm1hdHRpbmcrZ2d0aXRsZSgiQ2hhdEdQVCA0LjAgSUNEIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwgbl9kaWFnID0gbl9kaWFnKSsgY3VzdG9tX3RleHRfZm9ybWF0dGluZytnZ3RpdGxlKCJDbGF1ZGUzIEhhaWt1IHQxLjAgSUNEIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkLCBuX2RpYWcgPSBuX2RpYWcpKyBjdXN0b21fdGV4dF9mb3JtYXR0aW5nK2dndGl0bGUoIkNsYXVkZTMgT3B1cyB0MS4wIElDRCIsIHN1YikKdG9wX2RpYWdub3Npc19wbG90KGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsIG5fZGlhZyA9IG5fZGlhZykrIGN1c3RvbV90ZXh0X2Zvcm1hdHRpbmcrZ2d0aXRsZSgiR2VtaW5pIDEuMCBQcm8gSUNEIiwgc3ViKQp0b3BfZGlhZ25vc2lzX3Bsb3QoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCwgbl9kaWFnID0gbl9kaWFnKSsgY3VzdG9tX3RleHRfZm9ybWF0dGluZytnZ3RpdGxlKCJHZW1pbmkgMS41IFBybyBJQ0QiLCBzdWIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gMTB9Cm11bHRpX3RvcF9kaWFnbm9zaXNfcGxvdChkaXN0cmlidXRpb25fdmlzID0gInBvaW50cyIsIHdyYXBfd2lkdGg9NDUsIG5fZGlhZyA9IDI1LAogICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ3B0My41LCBkZl9ncHQ0LjAsIGRmX2NsYXVkZTNfaGFpa3VfdDEuMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMCwgZGZfZ2VtaW5pMS4wX3Byb190MS4wLAogICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS41X3Byb190MS4wKQpgYGAKCgoKYGBge3IsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gMTB9CnBsdF9kaWFnX2ljZCA8LSBtdWx0aV90b3BfZGlhZ25vc2lzX3Bsb3QoZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLCB3cmFwX3dpZHRoID0gMzMsIG5fZGlhZyA9IDE1LAogICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ3B0My41X2ljZCwgZGZfZ3B0NC4wX2ljZCwgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCkgKwogIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIpKSkKCnBsdF9kaWFnX2ljZApwbHRfZGlhZ19pY2QkZGF0YSAlPiUgCiAgc3VtbWFyaXNlKGZyZXE9bWVhbihmcmVxKSwuYnk9YygiY3JpdGVyaWEiLCJkaWFnbm9zaXMiKSkgJT4lIAogIGFycmFuZ2UoY3JpdGVyaWEsIGRlc2MoZnJlcSkpCmBgYAoKIyBDdW11bGF0aXZlIHRvcCBmcmVxdWVuY3kgcGxvdHMKCgpgYGB7ciwgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9My41fQpzdWIgPC0gIk9yaWdpbmFsIHJlc3BvbnNlcyIKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9ncHQzLjUpJHBsb3QrZ2d0aXRsZSgiR1BUMyIsIHN1YikKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9ncHQ0LjApJHBsb3QrZ2d0aXRsZSgiR1BUNCIsIHN1YikKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9jbGF1ZGUzX2hhaWt1X3QxLjApJHBsb3QrZ2d0aXRsZSgiQ2xhdWRlMyBIYWlrdSIsIHN1YikKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9jbGF1ZGUzX29wdXNfdDEuMCkkcGxvdCtnZ3RpdGxlKCJDbGF1ZGUzIEhhaWt1Iiwgc3ViKQpjdW11bGF0aXZlX2ZyZXF1ZW5jeV9wbG90KGRmX2dlbWluaTEuMF9wcm9fdDEuMCkkcGxvdCtnZ3RpdGxlKCJHZW1pbmkgUHJvIDEuMCIsIHN1YikKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9nZW1pbmkxLjVfcHJvX3QxLjApJHBsb3QrZ2d0aXRsZSgiR2VtaW5pIFBybyAxLjUiLCBzdWIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTMuNX0Kc3ViIDwtICJJQ0QgY29udmVydGVkIHJlc3BvbnNlcyIKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9ncHQzLjVfaWNkKSRwbG90K2dndGl0bGUoIkdQVDMgSUNEIiwgc3ViKQpjdW11bGF0aXZlX2ZyZXF1ZW5jeV9wbG90KGRmX2dwdDQuMF9pY2QpJHBsb3QrZ2d0aXRsZSgiR1BUNCBJQ0QiLCBzdWIpCmN1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCkkcGxvdCtnZ3RpdGxlKCJDbGF1ZGUzIEhhaWt1IElDRCIsIHN1YikKY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdChkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QpJHBsb3QrZ2d0aXRsZSgiQ2xhdWRlMyBIYWlrdSBJQ0QiLCBzdWIpCmN1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCkkcGxvdCtnZ3RpdGxlKCJHZW1pbmkgUHJvIDEuMCBJQ0QiLCBzdWIpCmN1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCkkcGxvdCtnZ3RpdGxlKCJHZW1pbmkgUHJvIDEuMCBJQ0QiLCBzdWIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zLjV9CnBsdF9mcmVxIDwtIG11bHRpX2N1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoCiAgbl9kaWFnbm9zZXMgPSAyNSwKICBkaXN0cmlidXRpb25fdmlzID0gInBvaW50cyIsCiAgZGZfZ3B0My41LAogIGRmX2dwdDQuMCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjAsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjAsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMAopICsKICBnZ3RpdGxlKCJPcmlnaW5hbCByZXNwb25zZXMiKQoKcGx0X2ZyZXEKcGx0X2ZyZXEkZGF0YSAlPiUgc3VtbWFyaXNlKGZyZXEgPSBtZWFuKHRvdGFsX2ZyZXF1ZW5jeSksIC5ieSA9ICJjcml0ZXJpYSIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTMuNX0KcGx0X2ZyZXFfaWNkIDwtIG11bHRpX2N1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoCiAgbl9kaWFnbm9zZXMgPSAyNSwKICBkaXN0cmlidXRpb25fdmlzID0gInBvaW50cyIsCiAgZGZfZ3B0My41X2ljZCwKICBkZl9ncHQ0LjBfaWNkLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAopICsKICBnZ3RpdGxlKCJJQ0QgY29udmVydGVkIHJlc3BvbnNlcyIpCgpwbHRfZnJlcV9pY2QKcGx0X2ZyZXFfaWNkJGRhdGEgJT4lIHN1bW1hcmlzZShmcmVxID0gbWVhbih0b3RhbF9mcmVxdWVuY3kpLCAuYnkgPSAiY3JpdGVyaWEiKQpgYGAKCiMgRGlhZ25vc2lzIHJhbmsgdGFibGUKCmBgYHtyfQpkaWFnbm9zaXNfcmFua190YWJsZShkZl9ncHQzLjUsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkKZGlhZ25vc2lzX3JhbmtfdGFibGUoZGZfZ3B0NC4wLCAibWFzdCB8bWFzdG9jfGFuYXBoeWxheGlzIikgJT4lIG11dGF0ZShkaWFnbm9zaXMgPSBzdWJzdHIoZGlhZ25vc2lzLCAxLCA2MCkpIApkaWFnbm9zaXNfcmFua190YWJsZShkZl9jbGF1ZGUzX2hhaWt1X3QxLjAsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkgCmRpYWdub3Npc19yYW5rX3RhYmxlKGRmX2NsYXVkZTNfb3B1c190MS4wLCAibWFzdCB8bWFzdG9jfGFuYXBoeWxheGlzIikgJT4lIG11dGF0ZShkaWFnbm9zaXMgPSBzdWJzdHIoZGlhZ25vc2lzLCAxLCA2MCkpIApkaWFnbm9zaXNfcmFua190YWJsZShkZl9nZW1pbmkxLjBfcHJvX3QxLjAsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkgCmRpYWdub3Npc19yYW5rX3RhYmxlKGRmX2dlbWluaTEuNV9wcm9fdDEuMCwgIm1hc3QgfG1hc3RvY3xhbmFwaHlsYXhpcyIpICU+JSBtdXRhdGUoZGlhZ25vc2lzID0gc3Vic3RyKGRpYWdub3NpcywgMSwgNjApKSAKYGBgCmBgYHtyfQpkaWFnbm9zaXNfcmFua190YWJsZShkZl9ncHQzLjVfaWNkLCAibWFzdCB8bWFzdG9jfGFuYXBoeWxheGlzIikgJT4lIG11dGF0ZShkaWFnbm9zaXMgPSBzdWJzdHIoZGlhZ25vc2lzLCAxLCA2MCkpIApkaWFnbm9zaXNfcmFua190YWJsZShkZl9ncHQ0LjBfaWNkLCAibWFzdCB8bWFzdG9jfGFuYXBoeWxheGlzIikgJT4lIG11dGF0ZShkaWFnbm9zaXMgPSBzdWJzdHIoZGlhZ25vc2lzLCAxLCA2MCkpIApkaWFnbm9zaXNfcmFua190YWJsZShkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLCAibWFzdCB8bWFzdG9jfGFuYXBoeWxheGlzIikgJT4lIG11dGF0ZShkaWFnbm9zaXMgPSBzdWJzdHIoZGlhZ25vc2lzLCAxLCA2MCkpIApkaWFnbm9zaXNfcmFua190YWJsZShkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkgCmRpYWdub3Npc19yYW5rX3RhYmxlKGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkgCmRpYWdub3Npc19yYW5rX3RhYmxlKGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QsICJtYXN0IHxtYXN0b2N8YW5hcGh5bGF4aXMiKSAlPiUgbXV0YXRlKGRpYWdub3NpcyA9IHN1YnN0cihkaWFnbm9zaXMsIDEsIDYwKSkgCmBgYAoKCmBgYHtyfQpyYW5rX3RhYmxlIDwtCiAgbXVsdGlfZGlhZ25vc2lzX3JhbmtfdGFibGUoCiAgICBzZWFyY2hfcGF0dGVybiA9ICJUNzhcXC4yIHxENDdcXC4wMiB8RDg5XFwuNDEgfEQ4OVxcLjQ5IHxEODlcXC40ICIsCiAgICBkZl9ncHQzLjVfaWNkLAogICAgZGZfZ3B0NC4wX2ljZCwKICAgIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAogICkKcmFua190YWJsZSAgCmBgYAoKYGBge3J9CnJhbmtfdGFibGUgJT4lIAogIGZsZXh0YWJsZSgpICU+JSAKICB3aWR0aCh3aWR0aCA9IDMwKSAlPiUgCiAgYWxpZ24oaiA9IDI6MywgYWxpZ24gPSAiY2VudGVyIiwgcGFydCA9ICJhbGwiKQpgYGAKCiMgRGl2ZXJzaXR5CgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9My41fQptdWx0aV9zaGFubm9uX3Bsb3QoCiAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogIHdyYXBfd2lkdGggPSA0NSwKICBuX2RpYWcgPSAyNSwKICBkZl9ncHQzLjUsCiAgZGZfZ3B0NC4wLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjAsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wCikKYGBgCgoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTMuNX0KcGx0X2Rpdl9pY2QgPC0gbXVsdGlfc2hhbm5vbl9wbG90KAogIGRpc3RyaWJ1dGlvbl92aXMgPSAicG9pbnRzIiwKICB3cmFwX3dpZHRoID0gNDUsCiAgbl9kaWFnID0gMjUsCiAgZGZfZ3B0My41X2ljZCwKICBkZl9ncHQ0LjBfaWNkLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAopCgpwbHRfZGl2X2ljZApwbHRfZGl2X2ljZCRkYXRhICU+JSBzdW1tYXJpc2Uoc2hhbm5vbj1tZWFuKHNoYW5ub24pLC5ieT0iY3JpdGVyaWEiKQpleHRyYWN0X2dncHVicl9wdmFsdWVzKHBsdF9kaXZfaWNkKSAgCmBgYAoKIyBTaW1pbGFyaXR5CgpgYGB7ciwgZmlnLndpZHRoPTQuMjUsIGZpZy5oZWlnaHQ9My41fQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2dwdDMuNSwgbWV0aG9kID0gImJyYXkiKQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2dwdDQuMCwgbWV0aG9kID0gImJyYXkiKQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2NsYXVkZTNfaGFpa3VfdDEuMCwgbWV0aG9kID0gImJyYXkiKQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2NsYXVkZTNfb3B1c190MS4wLCBtZXRob2QgPSAiYnJheSIpCmRpYWdub3Npc19zaW1pbGFyaXR5X2hlYXRtYXAoZGZfZ2VtaW5pMS4wX3Byb190MS4wLCBtZXRob2QgPSAiYnJheSIpCmRpYWdub3Npc19zaW1pbGFyaXR5X2hlYXRtYXAoZGZfZ2VtaW5pMS41X3Byb190MS4wLCBtZXRob2QgPSAiYnJheSIpCmBgYApgYGB7ciwgZmlnLndpZHRoPTQuMjUsIGZpZy5oZWlnaHQ9My41fQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2dwdDMuNV9pY2QsIG1ldGhvZCA9ICJicmF5IikKZGlhZ25vc2lzX3NpbWlsYXJpdHlfaGVhdG1hcChkZl9ncHQ0LjBfaWNkLCBtZXRob2QgPSAiYnJheSIpCmRpYWdub3Npc19zaW1pbGFyaXR5X2hlYXRtYXAoZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwgbWV0aG9kID0gImJyYXkiKQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwgbWV0aG9kID0gImJyYXkiKQpkaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsIG1ldGhvZCA9ICJicmF5IikKZGlhZ25vc2lzX3NpbWlsYXJpdHlfaGVhdG1hcChkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkLCBtZXRob2QgPSAiYnJheSIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LjI1LCBmaWcuaGVpZ2h0PTMuNX0KbXVsdGlfZGlhZ25vc2lzX3NpbWlsYXJpdHlfaGVhdG1hcCgKICBkZl9ncHQzLjUsCiAgZGZfZ3B0NC4wLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjAsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wLAogIG1ldGhvZCA9ICJicmF5IiwKICBzaG93X2RlbmQgPSBGLAogIGxhYmVsX3NpemUgPSA2LAogIHRpdGxlX3NpemUgPSA5CikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQuMjUsIGZpZy5oZWlnaHQ9My41fQptdWx0aV9kaWFnbm9zaXNfc2ltaWxhcml0eV9oZWF0bWFwKAogIGRmX2dwdDMuNV9pY2QsCiAgZGZfZ3B0NC4wX2ljZCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QsCiAgICBtZXRob2QgPSAiYnJheSIsCiAgc2hvd19kZW5kID0gRiwKICBsYWJlbF9zaXplID0gNiwKICB0aXRsZV9zaXplID0gOQopCmBgYAotIEJyYXktQ3VydGlzIHNpbWlsYXJpdHkgbWVhc3VyZXMgdGhlIHNpbWlsYXJpdHkgb2YgYSBnaXZlbiBkaWFnbm9zdGljIGNyaXRlcmlh4oCZcyBzZXQgb2YgYWx0ZXJuYXRpdmUgZGlhZ25vc2VzIGFsb25nIHdpdGggdGhlaXIgZnJlcXVlbmNpZXMuCi0gVGhpcyBkZW1vbnN0cmF0ZXMgdGhhdCBTTEUgY3JpdGVyaWEgcmVzdWx0cyBpbiBhIHZlcnkgc2ltaWxhciBzZXQgYW5kIGZyZXF1ZW5jeSBvZiBkaWFnbm9zZXMsIHdoaWxlIHRoZSBkaWFnbm9zZXMgYXNzb2NpYXRlZCB3aXRoIHR3byBNQ0FTIGNyaXRlcmlhIGFyZSBhcyBkaWZmZXJlbnQgZnJvbSBlYWNoIG90aGVyIGFzIHRoZXkgYXJlIGZyb20gdGhvc2UgZ2VuZXJhdGVkIGJ5IHRoZSBjcml0ZXJpYSBvZiBvdGhlciBjb25kaXRpb25zLgoKIyMjIyBNdWx0aSBCcmF5IEN1cnRpcyBTaW1pbGFyaXR5CgpgYGB7cn0KY2FsY3VsYXRlX3NpbWlsYXJpdHkgPC0gZnVuY3Rpb24oZGYpewogIGRmIDwtIGRmICU+JSAKICAgIHJlbmFtZShtb2RlbCA9IG9yaWdpbmFsX2RmKSAlPiUgCiAgICBjb3VudChtb2RlbCwgY3JpdGVyaWEsIGRpYWdub3NpcykgJT4lIAogICAgdW5pdGUoY3JpdGVyaWEsIGNyaXRlcmlhLCBtb2RlbCkKICAKICB0YWJsZShkZiRjcml0ZXJpYSwgZGYkZGlhZ25vc2lzKSAlPiUgCiAgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImJyYXkiKSAlPiUgCiAgYXMubWF0cml4KCkgJT4lIAogIHsxLS59IAp9CmBgYAoKYGBge3IsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9NC41fQpjb21iaW5lX2RhdGFfZnJhbWVzKAogIGRmX2dwdDMuNV9pY2QsCiAgZGZfZ3B0NC4wX2ljZCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QKKSAlPiUgCiAgY2FsY3VsYXRlX3NpbWlsYXJpdHkoKSAlPiUgCiAgbW9kZWxfY3JpdGVyaWFfaGVhdG1hcCguLCAKICAgICAgICAgICAgICAgIGNvbG9yX3NjYWxlID0gdmlyaWRpczo6dmlyaWRpcygzKSwgCiAgICAgICAgICAgICAgICB0aXRsZSA9ICIgIiwgCiAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQnJheS1DdXJ0aXNcbnNpbWlsYXJpdHkiLCAKICAgICAgICAgICAgICAgIHN5bW1ldHJpYyA9IEYsCiAgICAgICAgICAgICAgICBmb250X3NpemUgPSA4KSAKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD00LjV9CmNvbWJpbmVfZGF0YV9mcmFtZXMoCiAgZGZfZ3B0My41LAogIGRmX2dwdDQuMCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjAsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjAsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMAopICU+JSAKICBjYWxjdWxhdGVfc2ltaWxhcml0eSgpICU+JSAKICBtb2RlbF9jcml0ZXJpYV9oZWF0bWFwKC4sIAogICAgICAgICAgICAgICAgY29sb3Jfc2NhbGUgPSB2aXJpZGlzOjp2aXJpZGlzKDMpLCAKICAgICAgICAgICAgICAgIHRpdGxlID0gIiAiLCAKICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJCcmF5LUN1cnRpc1xuc2ltaWxhcml0eSIsIAogICAgICAgICAgICAgICAgc3ltbWV0cmljID0gRiwKICAgICAgICAgICAgICAgIGZvbnRfc2l6ZSA9IDgpIApgYGAKIyMjIFBDQQoKYGBge3IsIGZpZy53aWR0aD00LjI1LCBmaWcuaGVpZ2h0PTMuNX0KZGlhZ25vc2lzX3BjYV9wbG90KGRmX2dwdDMuNSkgKyBnZ3RpdGxlKCJHUFQzIikKZGlhZ25vc2lzX3BjYV9wbG90KGRmX2dwdDQuMCkgKyBnZ3RpdGxlKCJHUFQ0IikKZGlhZ25vc2lzX3BjYV9wbG90KGRmX2NsYXVkZTNfaGFpa3VfdDEuMCkgKyBnZ3RpdGxlKCJDbGF1ZGUgSGFpa3UiKQpkaWFnbm9zaXNfcGNhX3Bsb3QoZGZfY2xhdWRlM19vcHVzX3QxLjApICsgZ2d0aXRsZSgiQ2xhdWRlIE9wdXMiKQpkaWFnbm9zaXNfcGNhX3Bsb3QoZGZfZ2VtaW5pMS4wX3Byb190MS4wKSArIGdndGl0bGUoIkdlbWluaSIpCmRpYWdub3Npc19wY2FfcGxvdChkZl9nZW1pbmkxLjVfcHJvX3QxLjApICsgZ2d0aXRsZSgiR2VtaW5pIikKYGBgCgoKYGBge3J9CmRmIDwtIGxpc3ROKGRmX2dwdDMuNV9pY2QsIGRmX2dwdDQuMF9pY2QsIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCkgJT4lIAogIG1hcHBseShmdW5jdGlvbih4LHkpIHttdXRhdGUoeCwgbW9kZWw9eSl9LCAuLCBuYW1lcyguKSwgU0lNUExJRlkgPSBGKSAlPiUgCiAgYmluZF9yb3dzKCkgJT4lIAogIGNvdW50KG1vZGVsLCBjcml0ZXJpYSwgZGlhZ25vc2lzKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJkaWFnbm9zaXMiLCB2YWx1ZXNfZnJvbSA9ICJuIiwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUgCiAgdW5pdGUoaWQsIG1vZGVsLCBjcml0ZXJpYSwgc2VwID0gIl9fIikgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygiaWQiKSAlPiUgCiAgcHJjb21wKHNjYWxlLiA9IEYpCgphcy5kYXRhLmZyYW1lKGRmJHgpICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiaWQiKSAlPiUgCiAgc2VwYXJhdGUoaWQsIGludG8gPSBjKCJtb2RlbCIsICJjcml0ZXJpYSIpLCBzZXAgPSAiX18iKSAlPiUgCiAgZm9ybWF0X2NyaXRlcmlhKCkgJT4lIAogIGZvcm1hdF9tb2RlbHMoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvciA9IGNyaXRlcmlhKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICAjIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoKSArCiAgICB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKIyBQcmVjaXNpb24KCi0gUHJlY2lzaW9uIHJlcHJlc2VudHMgaG93IHNpbWlsYXIgZWFjaCBpdGVyYXRpb24gb2YgYSAxMC1wb2ludCBkaWZmZXJlbnRpYWwgZGlhZ25vc2lzIGlzIHdpdGggYWxsIG90aGVyIGRpZmZlcmVudGlhbCBkaWFnbm9zZXMgZnJvbSB0aGUgc2FtZSBzZXQgb2YgY3JpdGVyaWEuIAotIEkuZS4gaG93IHJlcHJvZHVjaWJsZSB0aGUgMTAtcG9pbnQgZGlmZmVyZW50aWFsIGRpYWdub3NpcyBpcyBmb3IgZWFjaCBjcml0ZXJpYQotIE1lYXN1cmVkIGJ5IG9idGFpbmluZyB0aGUgQnJheS1DdXJ0aXMgc2ltaWxhcml0eSB2YWx1ZXMgYmV0d2VlbiBhbGwgaXRlcmF0aW9ucyB3aXRoaW4gYSBjcml0ZXJpYQoKYGBge3IsIGV2YWw9Rn0KIyBTY3JpcHQgZm9yIGNhbGN1bGF0aW5nIGFsbCBCcmF5LUN1cnRpcyBzaW1pbGFyaXR5IHZhbHVlcyB3aXRoaW4gYSBjcml0ZXJpYQojIEZvdW5kIGluIHNvdXJjZShoZXJlKCJzY3JpcHRzL2RpdmVyc2l0eV9hbmFseXNpcy9jYWxjdWxhdGVfcHJlY2lzaW9uLlIiKSkKIyBDYWxjdWxhdGUgcHJlY2lzaW9uCmxpYnJhcnkoaGVyZSkKc291cmNlKGhlcmUoInV0aWxzL2RhdGFfcHJvY2Vzc2luZy5SIikpCgptb2RlbHMgPC0gbGlzdC5maWxlcyhoZXJlKCJkYXRhL3Byb2Nlc3NlZF9kaWFnbm9zZXMiKSwgcGF0dGVybiA9ICJneiQiKSAlPiUgCiAgc3RyX3NwbGl0KCJkaWFnbm9zZXNffF9pY2R8LmNzdiIpICU+JSAKICBzYXBwbHkoLiwgZnVuY3Rpb24oeCkgeFsyXSkgJT4lIAogIHVuaXF1ZSgpCgp1c2VfaWNkIDwtIFRSVUUKCmlmICh1c2VfaWNkKXttb2RlbHMgPC0gc3RyX2dsdWUoInttb2RlbHN9X2ljZCIpfQoKZm9yIChtIGluIG1vZGVscyl7CiAgcHJpbnQoc3ByaW50ZigiUkVBRElORyBJTiBEQVRBIEZPUjogJXMiLCBtKSkKICByZWFkX3BhdGggPC0gc3ByaW50ZigiZGF0YS9wcm9jZXNzZWRfZGlhZ25vc2VzL2RpYWdub3Nlc18lcy5jc3YuZ3oiLCBtKQogIGRmIDwtIHJlYWRfY3N2KGhlcmUocmVhZF9wYXRoKSkKICAKICBwcmludChzcHJpbnRmKCJDQUxDVUxBVElORyBQUkVDSVNJT04gRk9SOiAlcyIsIG0pKQogIGRmIDwtIGNhbGN1bGF0ZV9wcmVjaXNpb24oZGYpCiAgCiAgcHJpbnQoc3ByaW50ZigiV1JJVElORyBQUkVDSVNJT04gREFUQSBGT1I6ICVzIiwgbSkpCiAgb3V0X3BhdGggPC0gc3ByaW50ZigiZGF0YS9kaXZlcnNpdHlfYW5hbHlzaXMvZGlhZ25vc2lzX3ByZWNpc2lvbl8lcy5jc3YuZ3oiLCBtKQogIHdyaXRlX2NzdihkZiwgaGVyZShvdXRfcGF0aCkpCn0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9My41fQpwcmVjaXNpb25fZGlzdF90b19zaW0gPC0gZnVuY3Rpb24oZGYpewogIGRmICU+JSAKICAgIG11dGF0ZSgKICAgICAgbWVhbiA9IDEtbWVhbiwKICAgICAgbWF4ID0gMS1taW4sCiAgICAgIG1pbiA9IDEtbWF4CiAgICApCn0KCnBsdF9wcmVjaXNpb25faWNkIDwtIHJlYWRfY3N2KGhlcmUoImRhdGEvZGl2ZXJzaXR5X2FuYWx5c2lzL2NvbXBpbGVkX2ljZF9kaWFnbm9zaXNfcHJlY2lzaW9uLmNzdiIpKSAlPiUgCiAgcHJlY2lzaW9uX2Rpc3RfdG9fc2ltKCkgJT4lIAogIGZvcm1hdF9jcml0ZXJpYSgpICU+JSAKICBmb3JtYXRfbW9kZWxzKCkgJT4lCiAgZmlsdGVyKG1vZGVsICE9ICJHZW1pbmkgMS41IEZsYXNoIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGNyaXRlcmlhLCB5ID0gbWVhbikpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9IDQ1LCBoanVzdCA9IDEpKSsKICBsYWJzKHg9IiIsIHkgPSAiQXZlcmFnZSBCcmF5LUN1cnRpcyBTaW1pbGFyaXR5IikgKwogIGdncHVicjo6Z2VvbV9wd2MoYWVzKGdyb3VwID0gY3JpdGVyaWEpLCBtZXRob2QgPSAid2lsY294LnRlc3QiLCBwLmFkanVzdC5tZXRob2QgPSAiQkgiLCBoaWRlLm5zID0gVCwgbGFiZWwgPSAicC5hZGouc2lnbmlmIiwgYnJhY2tldC5udWRnZS55ID0gMC4zLCB2anVzdCA9IDAuNiwgc3RlcC5pbmNyZWFzZSA9IDAuMTQsIHRpcC5sZW5ndGggPSAwLjAyKSArCiAgbGFicyhjb2xvcj1OVUxMKSsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICBwbG90X3NlbGVjdG9yKCJwb2ludHMiKQoKcGx0X3ByZWNpc2lvbl9pY2QKcGx0X3ByZWNpc2lvbl9pY2QkZGF0YSAlPiUgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKG1lYW4pLCAuYnk9ImNyaXRlcmlhIikgCmV4dHJhY3RfZ2dwdWJyX3B2YWx1ZXMocGx0X3ByZWNpc2lvbl9pY2QpIApgYGAKCgojIGlORVhUCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9CmluZXh0X3Bsb3RzIDwtIGZ1bmN0aW9uKGluZXh0X29iail7CiAgZm9yIChpIGluIDE6Myl7CiAgICBwbHQgPC0gaU5FWFQ6OmdnaU5FWFQoaW5leHRfb2JqLCB0eXBlPWksIGZhY2V0LnZhcj0iQXNzZW1ibGFnZSIsIGNvbG9yLnZhcj0iQXNzZW1ibGFnZSIpICsKICAgICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSsKICAgICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQogICAgcHJpbnQocGx0KQogIH0KfQoKcmVhZFJEUyhoZXJlKCJkYXRhL2RpdmVyc2l0eV9hbmFseXNpcy9tY2FzX2lORVhUX2dwdDRfZTI1MDAwMC5SRFMiKSkgJT4lIGluZXh0X3Bsb3RzKCkKcmVhZFJEUyhoZXJlKCJkYXRhL2RpdmVyc2l0eV9hbmFseXNpcy9tY2FzX2lORVhUX2Ryb3BTaW5nbGVfZ3B0NF9lMjAwMDAwLlJEUyIpKSAlPiUgaW5leHRfcGxvdHMoKQpyZWFkUkRTKGhlcmUoImRhdGEvZGl2ZXJzaXR5X2FuYWx5c2lzL21jYXNfaU5FWFRfZHJvcFNpbmdsZV9wc3VlZG9NaW51c19ncHQ0X2UyMDAwMDAuUkRTIikpICU+JSBpbmV4dF9wbG90cygpCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoPTcuNCwgZmlnLmhlaWdodD02LjV9CiMgY3VzdG9tX2xhYmVsZXIgPC0gZnVuY3Rpb24oeCwgd3JhcF93aWR0aD0zMykgewojICAgICB4ICU+JQojICAgICAgICAgc3RyX3JlcGxhY2UoIl9fXy4rJCIsICIiKSAlPiUKIyAgICAgICAgIHN0cl93cmFwKHdpZHRoID0gd3JhcF93aWR0aCkKIyB9CmBgYAoKIyBGaW5hbCBwbG90CgojIyMgVmVyc2lvbiAxCgpgYGB7ciwgZmlnLndpZHRoPTcuNSwgZmlnLmhlaWdodD04LjUsIG1lc3NhZ2UgPSBGfQpuX2RpYWdub3Nlc19iYXIgPC0gMTAKbl9kaWFnbm9zZXNfYWJ1bmRhbmNlIDwtIDUwCm5fZGlhZ25vc2VzX2N1bXVsYXRpdmUgPC0gNTAKCnRpdGxlX3NpemUgPC0gOQpsYWJlbF9zaXplIDwtIDYKbGVnZW5kX3hfcGFkIDwtIDQKbGVnZW5kX3lfcGFkIDwtIDIKCmFwcGx5X3RleHRfZm9ybWF0dGluZyA8LSBsaXN0KHRoZW1lKAogIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gbGFiZWxfc2l6ZSksCiAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gdGl0bGVfc2l6ZSksCiAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUpLAogIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUrMSksCiAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNCwgJ2NtJyksCiAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEpLAogIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4odCA9IGxlZ2VuZF95X3BhZCwgciA9IGxlZ2VuZF94X3BhZCwgYiA9IGxlZ2VuZF95X3BhZCwgbCA9IGxlZ2VuZF94X3BhZCoxLjEpLAogIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KDAsICdjbScpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSG9yaXpvbnRhbCBzcGFjaW5nIGJldHdlZW4gbGVnZW5kIGl0ZW1zCiAgIyBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLCAnY20nKSwKICAjIGxlZ2VuZC5ib3guc3BhY2luZyA9IHVuaXQoMCwgImNtIikKICApKQoKc3RyaXBfbWFyZ2luIDwtIDEKc3RyaXBfZm9ybWF0dGluZyA8LSBsaXN0KHRoZW1lKAogIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpLCAKICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKQogICMgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpCikpCgpwbHRfZGlhZ3MgPC0KICBtdWx0aV90b3BfZGlhZ25vc2lzX3Bsb3QoCiAgICBkaXN0cmlidXRpb25fdmlzID0gInBvaW50cyIsCiAgICB3cmFwX3dpZHRoID0gNTgsCiAgICBuX2RpYWcgPSBuX2RpYWdub3Nlc19iYXIsCiAgICBkZl9ncHQzLjVfaWNkLAogICAgZGZfZ3B0NC4wX2ljZCwKICAgIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgYXBwbHlfdGV4dF9mb3JtYXR0aW5nICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNi41KSkgKwogIHN0cmlwX2Zvcm1hdHRpbmcgKwogICMgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygtMSwwKSkrCiAgdGhlbWUocGFuZWwuc3BhY2luZyA9IHVuaXQoMCwgImxpbmVzIikpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMikpKSAgIyBJbmNyZWFzZSB0aGUgcG9pbnQgc2l6ZSBpbiB0aGUgbGVnZW5kKQogIAoKcGx0X3JhbmsgPC0KICBtdWx0aV9yYW5rZWRfYWJ1bmRhbmNlX3Bsb3QoCiAgICBkZl9ncHQzLjVfaWNkLAogICAgZGZfZ3B0NC4wX2ljZCwKICAgIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgYXBwbHlfdGV4dF9mb3JtYXR0aW5nICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQoKcGx0X2N1bXVsYXRpdmUgPC0gbXVsdGlfY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdCgKICBuX2RpYWdub3NlcyA9IG5fZGlhZ25vc2VzX2N1bXVsYXRpdmUsCiAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogIGRmX2dwdDMuNV9pY2QsCiAgZGZfZ3B0NC4wX2ljZCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QKKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICBsYWJzKHkgPSAiQ29tYmluZWQgZnJlcXVlbmN5XG5vZiB0b3AgNTAgZGlhZ25vc2VzIiwgeCA9IE5VTEwpCgpwbHRfc2hhbm5vbiA8LSBtdWx0aV9zaGFubm9uX3Bsb3QoCiAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogIHdyYXBfd2lkdGggPSA0NSwKICBuX2RpYWcgPSAyNSwKICBkZl9ncHQzLjVfaWNkLAogIGRmX2dwdDQuMF9pY2QsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCikgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQoKcGx0X3ByZWNpc2lvbiA8LSByZWFkX2NzdihoZXJlKCJkYXRhL2RpdmVyc2l0eV9hbmFseXNpcy9jb21waWxlZF9pY2RfZGlhZ25vc2lzX3ByZWNpc2lvbi5jc3YiKSkgJT4lIAogIHByZWNpc2lvbl9kaXN0X3RvX3NpbSgpICU+JSAKICBmb3JtYXRfY3JpdGVyaWEoKSAlPiUgCiAgZm9ybWF0X21vZGVscygpICU+JQogIGZpbHRlcihtb2RlbCAhPSAiR2VtaW5pIDEuNSBGbGFzaCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjcml0ZXJpYSwgeSA9IG1lYW4pKSsKICB0aGVtZV9idygpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPSA0NSwgaGp1c3QgPSAxKSkrCiAgbGFicyh4PSIiLCB5ID0gIkF2ZXJhZ2UgQnJheS1DdXJ0aXNcblNpbWlsYXJpdHkiKSArCiAgZ2dwdWJyOjpnZW9tX3B3YyhhZXMoZ3JvdXAgPSBjcml0ZXJpYSksIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIsIGhpZGUubnMgPSBULCBsYWJlbCA9ICJwLmFkai5zaWduaWYiLCBicmFja2V0Lm51ZGdlLnkgPSAwLjMsIHZqdXN0ID0gMC42LCBzdGVwLmluY3JlYXNlID0gMC4xNCwgdGlwLmxlbmd0aCA9IDAuMDIpICsKICBsYWJzKGNvbG9yPU5VTEwpKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHBsb3Rfc2VsZWN0b3IoInBvaW50cyIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkKCmZ1bGxfcGx0IDwtIHBsb3RfZ3JpZCgKICAKICAjIyMKICBwbHRfZGlhZ3MsCiAgIyMjCiAgTlVMTCwKICBwbG90X2dyaWQoCiAgICAgIHBsdF9yYW5rLAogICAgICBwbHRfY3VtdWxhdGl2ZSwKICAgICAgcGx0X3NoYW5ub24sCiAgICAgIHBsdF9wcmVjaXNpb24sCiAgICAgIG5yb3cgPSAxLCAKICAgICAgYXhpcyA9ICd0YicsCiAgICAgIGFsaWduID0gJ2gnLAogICAgICByZWxfd2lkdGhzID0gYygxLCAwLjcsIDAuNywgMC43KSwKICAgICAgbGFiZWxzID0gYyhMRVRURVJTWzI6NV0pLAogICAgICB2anVzdCA9IDAuMgogICAgKSwKICBuY29sID0gMSwKICByZWxfaGVpZ2h0cyA9IGMoMS4yLCAwLjA1LCAwLjY1KSwKICBsYWJlbHMgPSBjKCJBIiwiIiwiIikKKSAgCgpmdWxsX3BsdApgYGAKCiMjIyBWZXJzaW9uIDIKYGBge3IsIGZpZy53aWR0aD03LjUsIGZpZy5oZWlnaHQ9OC41LCBtZXNzYWdlID0gRn0Kbl9kaWFnbm9zZXNfYmFyIDwtIDEwCm5fZGlhZ25vc2VzX2FidW5kYW5jZSA8LSA1MApuX2RpYWdub3Nlc19jdW11bGF0aXZlIDwtIDUwCgp0aXRsZV9zaXplIDwtIDkKbGFiZWxfc2l6ZSA8LSA2CmxlZ2VuZF94X3BhZCA8LSA0CmxlZ2VuZF95X3BhZCA8LSAyCgphcHBseV90ZXh0X2Zvcm1hdHRpbmcgPC0gbGlzdCh0aGVtZSgKICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUpLAogIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHRpdGxlX3NpemUpLAogIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKSwKICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKzEpLAogIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjQsICdjbScpLAogIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIsIHNpemUgPSAxKSwKICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKHQgPSBsZWdlbmRfeV9wYWQsIHIgPSBsZWdlbmRfeF9wYWQsIGIgPSBsZWdlbmRfeV9wYWQsIGwgPSBsZWdlbmRfeF9wYWQqMS4xKSwKICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgwLCAnY20nKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEhvcml6b250YWwgc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBpdGVtcwogICMgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMCwgJ2NtJyksCiAgIyBsZWdlbmQuYm94LnNwYWNpbmcgPSB1bml0KDAsICJjbSIpCiAgKSkKCnN0cmlwX21hcmdpbiA8LSAxCnN0cmlwX2Zvcm1hdHRpbmcgPC0gbGlzdCh0aGVtZSgKICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKSwgCiAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gc3RyaXBfbWFyZ2luLCByID0gc3RyaXBfbWFyZ2luLCBiID0gc3RyaXBfbWFyZ2luLCBsID0gc3RyaXBfbWFyZ2luKSkKICAjIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKQopKQoKcGx0X2RpYWdzIDwtCiAgbXVsdGlfdG9wX2RpYWdub3Npc19wbG90KAogICAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogICAgd3JhcF93aWR0aCA9IDU4LAogICAgbl9kaWFnID0gbl9kaWFnbm9zZXNfYmFyLAogICAgZGZfZ3B0My41X2ljZCwKICAgIGRmX2dwdDQuMF9pY2QsCiAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLAogICAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkLAogICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwKICAgIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYuNSkpICsKICBzdHJpcF9mb3JtYXR0aW5nICsKICAjIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLTEsMCkpKwogIHRoZW1lKHBhbmVsLnNwYWNpbmcgPSB1bml0KDAsICJsaW5lcyIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIpLCBucm93ID0gMSkpICAjIEluY3JlYXNlIHRoZSBwb2ludCBzaXplIGluIHRoZSBsZWdlbmQpCiAgCgpwbHRfcmFuayA8LQogIG11bHRpX3JhbmtlZF9hYnVuZGFuY2VfcGxvdCgKICAgIGRmX2dwdDMuNV9pY2QsCiAgICBkZl9ncHQ0LjBfaWNkLAogICAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICAgIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICAgIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjcsMC43KSkrCiAgIyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxKSkgKwogIGxhYnMoY29sb3IgPSBOVUxMKQoKcGx0X2N1bXVsYXRpdmUgPC0gbXVsdGlfY3VtdWxhdGl2ZV9mcmVxdWVuY3lfcGxvdCgKICBuX2RpYWdub3NlcyA9IG5fZGlhZ25vc2VzX2N1bXVsYXRpdmUsCiAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogIGRmX2dwdDMuNV9pY2QsCiAgZGZfZ3B0NC4wX2ljZCwKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QKKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICBsYWJzKHkgPSAiQ29tYmluZWQgZnJlcXVlbmN5XG5vZiB0b3AgNTAgZGlhZ25vc2VzIiwgeCA9IE5VTEwpCgpwbHRfc2hhbm5vbiA8LSBtdWx0aV9zaGFubm9uX3Bsb3QoCiAgZGlzdHJpYnV0aW9uX3ZpcyA9ICJwb2ludHMiLAogIHdyYXBfd2lkdGggPSA0NSwKICBuX2RpYWcgPSAyNSwKICBkZl9ncHQzLjVfaWNkLAogIGRmX2dwdDQuMF9pY2QsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCikgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQoKcGx0X3ByZWNpc2lvbiA8LSByZWFkX2NzdihoZXJlKCJkYXRhL2RpdmVyc2l0eV9hbmFseXNpcy9jb21waWxlZF9pY2RfZGlhZ25vc2lzX3ByZWNpc2lvbi5jc3YiKSkgJT4lIAogIHByZWNpc2lvbl9kaXN0X3RvX3NpbSgpICU+JSAKICBmb3JtYXRfY3JpdGVyaWEoKSAlPiUgCiAgZm9ybWF0X21vZGVscygpICU+JQogIGZpbHRlcihtb2RlbCAhPSAiR2VtaW5pIDEuNSBGbGFzaCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjcml0ZXJpYSwgeSA9IG1lYW4pKSsKICB0aGVtZV9idygpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPSA0NSwgaGp1c3QgPSAxKSkrCiAgbGFicyh4PSIiLCB5ID0gIkF2ZXJhZ2UgQnJheS1DdXJ0aXNcblNpbWlsYXJpdHkiKSArCiAgZ2dwdWJyOjpnZW9tX3B3YyhhZXMoZ3JvdXAgPSBjcml0ZXJpYSksIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIsIGhpZGUubnMgPSBULCBsYWJlbCA9ICJwLmFkai5zaWduaWYiLCBicmFja2V0Lm51ZGdlLnkgPSAwLjMsIHZqdXN0ID0gMC42LCBzdGVwLmluY3JlYXNlID0gMC4xNCwgdGlwLmxlbmd0aCA9IDAuMDIpICsKICBsYWJzKGNvbG9yPU5VTEwpKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHBsb3Rfc2VsZWN0b3IoInBvaW50cyIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkKCmZ1bGxfcGx0IDwtIHBsb3RfZ3JpZCgKICAKICAjIyMKICBwbHRfZGlhZ3MsCiAgIyMjCiAgTlVMTCwKICBwbG90X2dyaWQoCiAgICAgIHBsdF9yYW5rLAogICAgICBwbG90X2dyaWQoCiAgICAgICAgcGxvdF9ncmlkKAogICAgICAgICAgcGx0X3NoYW5ub24rIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogICAgICAgICAgcGx0X3ByZWNpc2lvbisgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICAgICAgICBucm93ID0gMSwKICAgICAgICAgIGF4aXMgPSAndGInLAogICAgICAgICAgYWxpZ24gPSAnaCcKICAgICAgICApLAogICAgICAgIGdldF9sZWdlbmQocGx0X3NoYW5ub24rIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChyb3cgPSAxKSkpLAogICAgICAgIG5jb2wgPSAxLAogICAgICAgIHJlbF9oZWlnaHRzID0gYygxLDAuMSkKICAgICAgKSwKICAgICAgbnJvdyA9IDEsIAogICAgICByZWxfd2lkdGhzID0gYygxLDEpLAogICAgICAjIGxhYmVscyA9IGMoTEVUVEVSU1syOjVdKSwKICAgICAgdmp1c3QgPSAwLjIKICAgICksCiAgbmNvbCA9IDEsCiAgcmVsX2hlaWdodHMgPSBjKDEuMiwgMC4wNSwgMC42NSksCiAgbGFiZWxzID0gYygiQSIsIiIsIiIpCikgIAoKZnVsbF9wbHQKYGBgCgojIyMgVmVyc2lvbiAzCmBgYHtyLCBmaWcud2lkdGg9Ny41LCBmaWcuaGVpZ2h0PTguNSwgbWVzc2FnZSA9IEZ9Cm5fZGlhZ25vc2VzX2JhciA8LSAxMApuX2RpYWdub3Nlc19hYnVuZGFuY2UgPC0gNTAKbl9kaWFnbm9zZXNfY3VtdWxhdGl2ZSA8LSA1MAoKdGl0bGVfc2l6ZSA8LSA5CmxhYmVsX3NpemUgPC0gNgpsZWdlbmRfeF9wYWQgPC0gNApsZWdlbmRfeV9wYWQgPC0gMgoKYXBwbHlfdGV4dF9mb3JtYXR0aW5nIDwtIGxpc3QoCiAgdGhlbWUoCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gdGl0bGVfc2l6ZSksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gbGFiZWxfc2l6ZSwgbWFyZ2luPW1hcmdpbigwLDAsMCxyPTEpKSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUgKyAxKSwKICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjQsICdjbScpLAogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC40LCAnY20nKSwKICAgICMgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChzaXplID0gIG1hcmdpbigwLDAsMCwwKSksCiAgICBsZWdlbmQuYm94LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMSksCiAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKAogICAgICB0ID0gbGVnZW5kX3lfcGFkLAogICAgICByID0gbGVnZW5kX3hfcGFkLAogICAgICBiID0gbGVnZW5kX3lfcGFkLAogICAgICBsID0gbGVnZW5kX3hfcGFkCiAgICApLAogICAgbGVnZW5kLmtleS5zcGFjaW5nLnkgPSB1bml0KC0xLjUsICJwdCIpLAogICAgbGVnZW5kLmJveC5zcGFjaW5nID0gdW5pdCg1LCJwdCIpCiAgKQopCgpzdHJpcF9tYXJnaW4gPC0gMQpzdHJpcF9mb3JtYXR0aW5nIDwtIGxpc3QodGhlbWUoCiAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gc3RyaXBfbWFyZ2luLCByID0gc3RyaXBfbWFyZ2luLCBiID0gc3RyaXBfbWFyZ2luLCBsID0gc3RyaXBfbWFyZ2luKSksIAogIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpCiAgIyBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KG1hcmdpbiA9IG1hcmdpbih0ID0gc3RyaXBfbWFyZ2luLCByID0gc3RyaXBfbWFyZ2luLCBiID0gc3RyaXBfbWFyZ2luLCBsID0gc3RyaXBfbWFyZ2luKSkKKSkKCnBsdF9kaWFncyA8LQogIG11bHRpX3RvcF9kaWFnbm9zaXNfcGxvdCgKICAgIGRpc3RyaWJ1dGlvbl92aXMgPSAicG9pbnRzIiwKICAgIHdyYXBfd2lkdGggPSA1OCwKICAgIG5fZGlhZyA9IG5fZGlhZ25vc2VzX2JhciwKICAgIGRmX2dwdDMuNV9pY2QsCiAgICBkZl9ncHQ0LjBfaWNkLAogICAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICAgIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwKICAgIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2LjUpKSArCiAgc3RyaXBfZm9ybWF0dGluZyArCiAgIyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKC0xLDApKSsKICB0aGVtZShwYW5lbC5zcGFjaW5nID0gdW5pdCgwLCAibGluZXMiKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAyKSwgbnJvdyA9IDEpKSAgIyBJbmNyZWFzZSB0aGUgcG9pbnQgc2l6ZSBpbiB0aGUgbGVnZW5kKQogIAoKcGx0X3JhbmsgPC0KICBtdWx0aV9yYW5rZWRfYWJ1bmRhbmNlX3Bsb3QoCiAgICBkZl9ncHQzLjVfaWNkLAogICAgZGZfZ3B0NC4wX2ljZCwKICAgIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkLAogICAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43LDAuNykpKwogICMgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsKICBsYWJzKGNvbG9yID0gTlVMTCkKCnBsdF9jdW11bGF0aXZlIDwtIG11bHRpX2N1bXVsYXRpdmVfZnJlcXVlbmN5X3Bsb3QoCiAgbl9kaWFnbm9zZXMgPSBuX2RpYWdub3Nlc19jdW11bGF0aXZlLAogIGRpc3RyaWJ1dGlvbl92aXMgPSAicG9pbnRzIiwKICBkZl9ncHQzLjVfaWNkLAogIGRmX2dwdDQuMF9pY2QsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgYXBwbHlfdGV4dF9mb3JtYXR0aW5nICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKSArCiAgbGFicyh5ID0gIkNvbWJpbmVkIGZyZXF1ZW5jeVxub2YgdG9wIDUwIGRpYWdub3NlcyIsIHggPSBOVUxMKQoKcGx0X3NoYW5ub24gPC0gbXVsdGlfc2hhbm5vbl9wbG90KAogIGRpc3RyaWJ1dGlvbl92aXMgPSAicG9pbnRzIiwKICB3cmFwX3dpZHRoID0gNDUsCiAgbl9kaWFnID0gMjUsCiAgZGZfZ3B0My41X2ljZCwKICBkZl9ncHQ0LjBfaWNkLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2QsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZAopICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcgKwogIHRoZW1lKAogICAgIyBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgCiAgICAjIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsaGp1c3Q9MSkpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQoKcGx0X3ByZWNpc2lvbiA8LSByZWFkX2NzdihoZXJlKCJkYXRhL2RpdmVyc2l0eV9hbmFseXNpcy9jb21waWxlZF9pY2RfZGlhZ25vc2lzX3ByZWNpc2lvbi5jc3YiKSkgJT4lIAogIHByZWNpc2lvbl9kaXN0X3RvX3NpbSgpICU+JSAKICBmb3JtYXRfY3JpdGVyaWEoKSAlPiUgCiAgZm9ybWF0X21vZGVscygpICU+JQogIGZpbHRlcihtb2RlbCAhPSAiR2VtaW5pIDEuNSBGbGFzaCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjcml0ZXJpYSwgeSA9IG1lYW4pKSsKICB0aGVtZV9idygpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPSA5MCwgaGp1c3QgPSAxKSkrCiAgbGFicyh4PSIiLCB5ID0gIk1lYW4gQnJheS1DdXJ0aXMgU2ltaWxhcml0eSIpICsKICBnZ3B1YnI6Omdlb21fcHdjKGFlcyhncm91cCA9IGNyaXRlcmlhKSwgbWV0aG9kID0gIndpbGNveC50ZXN0IiwgcC5hZGp1c3QubWV0aG9kID0gIkJIIiwgaGlkZS5ucyA9IFQsIGxhYmVsID0gInAuYWRqLnNpZ25pZiIsIGJyYWNrZXQubnVkZ2UueSA9IDAuMywgdmp1c3QgPSAwLjYsIHN0ZXAuaW5jcmVhc2UgPSAwLjE0LCB0aXAubGVuZ3RoID0gMC4wMikgKwogIGxhYnMoY29sb3I9TlVMTCkrCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgcGxvdF9zZWxlY3RvcigicG9pbnRzIikgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQoKcGx0X3NpbWlsYXJpdHkgPC0gbXVsdGlfZGlhZ25vc2lzX3NpbWlsYXJpdHlfaGVhdG1hcCgKICBtZXRob2QgPSAiYnJheSIsCiAgc2hvd19kZW5kID0gVCwKICBkZW5kcm9ncmFtX3dlaWdodCA9IHVuaXQoMi41LCAibW0iKSwKICBsZWdlbmRfbGFiZWwgPSAiQnJheS1DdXJ0aXMgc2ltaWxhcml0eSIsCiAgbGVnZW5kX2RpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICBsYWJlbF9zaXplID0gNiwKICB0aXRsZV9zaXplID0gOSwKICBkZl9ncHQzLjVfaWNkLAogIGRmX2dwdDQuMF9pY2QsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZCwKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkCikKCmZ1bGxfcGx0IDwtIHBsb3RfZ3JpZCgKICAjIyMKICBwbHRfZGlhZ3MsCiAgIyMjCiAgTlVMTCwKICBwbG90X2dyaWQoCiAgICBOVUxMLAogICAgcGxvdF9ncmlkKAogICAgZ3JpZDo6Z3JpZC5ncmFiRXhwcihDb21wbGV4SGVhdG1hcDo6ZHJhdyhwbHRfc2ltaWxhcml0eSwgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICdib3R0b20nKSksCiAgICBOVUxMLG5jb2w9MSxyZWxfaGVpZ2h0cyA9IGMoMSwwLjEpCiAgICApLAogICAgcGxvdF9ncmlkKAogICAgcGx0X3JhbmssCiAgICBOVUxMLG5jb2w9MSxyZWxfaGVpZ2h0cyA9IGMoMSwwLjEpCiAgICApLAogICAgcGxvdF9ncmlkKAogICAgICBwbG90X2dyaWQoCiAgICAgICAgcGx0X3NoYW5ub24rIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogICAgICAgIHBsdF9wcmVjaXNpb24rIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogICAgICAgIG5yb3cgPSAxLAogICAgICAgIGF4aXMgPSAndGInLAogICAgICAgIGFsaWduID0gJ2gnCiAgICAgICksCiAgICAgIHBsb3RfZ3JpZChOVUxMLGdldF9sZWdlbmQocGx0X3NoYW5ub24pLG5yb3c9MSxyZWxfd2lkdGhzPWMoMC4yLDEpKSwKICAgICAgTlVMTCwKICAgICAgbmNvbCA9IDEsCiAgICAgIHJlbF9oZWlnaHRzID0gYygxLDAuMDUsMC4xKQogICAgKSwKICAgIG5yb3cgPSAxLCAKICAgIHJlbF93aWR0aHMgPSBjKDAuMSwwLjgsIDEsMC45KSwKICAgIHZqdXN0ID0gMC4yCiAgICApLAogIG5jb2wgPSAxLAogIHJlbF9oZWlnaHRzID0gYygxLjIsIDAuMDcsIDAuNjUpCikgIAoKZnVsbF9wbHQgPC0gY293cGxvdDo6Z2dkcmF3KGZ1bGxfcGx0KStjb3dwbG90OjpkcmF3X3Bsb3RfbGFiZWwoYygiQSIsIkIiLCJDIiwiRCIsIkUiKSwgeD1jKDAsMCwwLjM1LDAuNjcsMC44MyksIHk9YygxLDAuMzgsMC4zOCwwLjM4LDAuMzgpKQpmdWxsX3BsdApgYGAKCi0gQWRkIGEgMiBjb2x1bW4gbGVnZW5kIHVuZGVyIEQrRQoKYGBge3IsIGV2YWw9Rn0KZ2dzYXZlKHBsb3Q9ZnVsbF9wbHQsZmlsZW5hbWU9aGVyZSgiZmlndXJlcy8zX2RpYWdub3Npc19kaXZlcnNpdHkucGRmIiksIHdpZHRoID0gNy41LCBoZWlnaHQgPSA4LjUpCmBgYAoKc2V0X3RhYmxlX3Byb3BlcnRpZXMob3B0c19wZGYgPSBsaXN0KHRhYmNvbHNlcCA9IDApKQpgYGB7cn0Kc2V0X2ZsZXh0YWJsZV9kZWZhdWx0cyhmb250c19pZ25vcmU9VFJVRSkKCm11bHRpX2RpYWdub3Npc19yYW5rX3RhYmxlKHNlYXJjaF9wYXR0ZXJuID0gIlQ3OFxcLjIgfEQ0N1xcLjAyIHxEODlcXC40MSB8RDg5XFwuNDkgfEQ4OVxcLjQgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9ncHQzLjVfaWNkLCBkZl9ncHQ0LjBfaWNkLCBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLCBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QsIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2QsIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QpICU+JSAKICBmbGV4dGFibGUoKSAlPiUgCiAgd2lkdGgod2lkdGggPSAyKSAlPiUgCiAgZm9udHNpemUoc2l6ZSA9IDkpICU+JSAKICBmb250c2l6ZShzaXplID0gMTAsIHBhcnQgPSAiaGVhZGVyIikgJT4lIAogIHBhZGRpbmcocGFkZGluZyA9IDApICU+JSAKICBhbGlnbihqID0gMjozLCBhbGlnbiA9ICJjZW50ZXIiLCBwYXJ0ID0gImFsbCIpICU+JSAKICBzZXRfdGFibGVfcHJvcGVydGllcyhvcHRzX3BkZiA9IGxpc3QoYXJyYXlzdHJldGNoID0gMS4yNSkpICU+JSAKICB7cHJpbnQoLiwgcHJldmlldyA9ICJwZGYiKTsufQpgYGAKCgo=